<?php defined('SMIT_PATH') or die('Access Denied');
/**
 *	SmitBase.php - Welcome to the Grand Central
 *	This is Smit Matterhorn - Build 24109
 *	
 *
 * @author		The SmitBoard Team & Community
 * @license		GNU GPL v3
 * @link		http://smitboard.sf.net
 * @since		Matterhorn
 */
 
class SmitBase {

	private static $instance;

	public $vars = array();
	
	public $appname;

	public $app;
	
	public $classes;

	public $lib;

	public $bbcode;

	public $sha2;

	public $output;

	public $debug;

	public $session;

	public $cache;

	public $db;

	public $lang_dir;

	public $lang = array();

	public $db_vars = array();

	private function __construct()
	{
		// Prevent direct call
	}

    public function __clone()
    {
        trigger_error('cloning denied.', E_USER_ERROR);
    }

	static function getInstance()
	{
		if(!is_object(self::$instance))
		{
			self::$instance = new self;
		}
		// Return singleton object
		return self::$instance;
	}
	
	function php_error($errno, $errstr, $errfile, $errline)
	{
		$this->error($errstr . ' in ' . $errfile . ' [' . $errline . ']', $errno);
	}

	function error($message, $error = E_SMIT, $dev = NULL)
	{
		switch($error)
		{
			default:
			case E_SMIT:
				$code = 'Smit Error';
			break;
			case E_MISSING_FILE:
				$code = 'Missing File';
			break;
			case E_DATABASE:
				$code = 'Database Error';
			break;
			case E_CLASS_MISMATCH:
				$code = 'Class Missmatch';
			break;
			case E_OUTPUT:
				$code = 'Output Error';
			break;
			case E_CACHE:
				$code = 'Caching Error';
			break;
		}
		// the HTML
		$debug = '';
		$extra = '';

		// Print extra info, if Debug mode is on
		if(SMIT_DEBUG)
		{
			if($dev != NULL)
			{
				$debug .= '<span class="b">Debug Mode: </span><p><code>' . $dev . '</code></p>';
			}
			$extra = ' - <span style="color: #E13300">Debug Mode</span>';
		}
		if(SMIT_DEBUG_LOG)
		{
			$error_log = SMIT_PATH . 'error.log.php';
			$current = str_replace(array('<?php /*' . chr(13) . chr(10), chr(13) . chr(10) . '*/ ?>'), '', file_get_contents($error_log));
			$current .= '[' . date('D M d h:i:s Y') . '] ' . $code . ': ' . $message . chr(13) . chr(10);
			file_put_contents($error_log, '<?php /*' . chr(13) . chr(10) . $current . chr(13) . chr(10) . '*/ ?>');
		}
		$out = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
		<html><head><title>Critical Error | SmitBoard</title><style type="text/css">
		body { background: #EEE;font: 13px "Trebuchet MS", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; }
		#wrapper { background: #FFF; color: #444;font-weight: 700;width: 60%; margin: 100px auto; padding: 20px; }
		.heading{ font-weight: 700; color: #FFF;font-size: 23px;padding: 20px;margin: 0 0 10px 0;background: #83A342; }
		.bottom { text-align: right; color: #555;padding: 10px;border: 1px solid #CCCCCC;font: bold 13px "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;background: #F7F7F7; }
		.b { color: #8F5B00;font-size: 17px;font-weight: 700;text-transform: uppercase; }
		code { border: 1px solid #E2C822; display: block;background: #FFF9D7; padding: 10px; }
		</style></head><body><div id="wrapper"><div class="heading">' . $code . '</div><p>' . $message . '</p>' . $debug . '<div class="bottom">Powered by SmitBoard Matterhorn ' . $extra . '</div></body></html>';
		
		print $out;
		exit;
	}

	function init()
	{
		// Reverse the Effects of Magic Quotes GPC
		if(function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc())
		{
			foreach($_GET as $key => $value)
			{
				$_GET[$key] = stripslashes($value);
			}
			foreach($_POST as $key => $value)
			{
				$_POST[$key] = stripslashes($value);
			}
			foreach($_COOKIE as $key => $value)
			{
				$_COOKIE[$key] = stripslashes($value);
			}
		}
		
		// PHP >= 5.1.0 Only
		if(function_exists('date_default_timezone_set')) date_default_timezone_set('UTC');
		
		// Load the main configuration file
		$this->loadConfig(SMIT_PATH . 'root_config' . EXT, 'root');

		// Attempt to check if we are installed by testing if a db driver is set
		if(!isset($this->vars['root']['db_driver']) || $this->vars['root']['db_driver'] == NULL)
		{
			$protocol = ($_SERVER['SERVER_PORT'] == 80) ? 'http' : 'https';
			$dir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])) . '/';
			header('location: ' . $protocol . '://' . $_SERVER['HTTP_HOST'] . $dir . 'installer/setup.php');
		}
		// Load the Core classes
		$this->load('debug');
		$this->load('cache');
		$this->load('database');
		$this->load('corelib');
		$this->load('output');

		// Check if we need to emulate PATH_INFO, SEF Links for all!
		if(!isset($_SERVER['PATH_INFO'])) $this->emulate_PI(basename($this->lib->input_server('SCRIPT_NAME')));
		
		// Load the DB Stored Configuration
		// Nearly There...
		$this->db->loadconfig();		
		
		// Set the Language
		// Hold on!!
		$this->langSet($this->db->config['language']);

		// Set the template Directory
		$this->output->setDir($this->db->config['current_style']);

		// The Session class has a construct, so we load it last...
		$this->load('session');

		$this->debug->general('Smit Core Initiated, This is 
		Smit ' . SMIT_HUMAN_VERSION . ' ' . SMIT_BUILD . '
		on PHP ' . $this->lib->phpversion);
		// Smit is ready!
	}

	function init_generic()
	{
		// Reverse the Effects of Magic Quotes GPC
		if(function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc())
		{
			foreach($_GET as $key => $value)
			{
				$_GET[$key] = stripslashes($value);
			}
			foreach($_POST as $key => $value)
			{
				$_POST[$key] = stripslashes($value);
			}
			foreach($_COOKIE as $key => $value)
			{
				$_COOKIE[$key] = stripslashes($value);
			}
		}
		
		// PHP >= 5.1.0 Only
		if(function_exists('date_default_timezone_set')) date_default_timezone_set('UTC');
		
			$protocol = ($_SERVER['SERVER_PORT'] == 80) ? 'http' : 'https';
			$this->vars['root'] = array(
				'root_url' => $protocol . '://' . $_SERVER['HTTP_HOST'] . str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])) . '/'
			);
		// Load the Core classes
		$this->load('debug');
		$this->load('corelib');
		
		$this->langSet('en-gb');

		// Check if we need to emulate PATH_INFO, SEF Links for all!
		if(!isset($_SERVER['PATH_INFO'])) $this->emulate_PI(basename($this->lib->input_server('SCRIPT_NAME')));

		//$this->debug->general('Smit Core Initiated, This is 
		//Smit ' . SMIT_HUMAN_VERSION . ' ' . SMIT_BUILD . '
		//on PHP ' . $this->lib->phpversion);
		// Smit is ready!
	}

	function loadConfig($file, $name)
	{
		// Check if we already have the file loaded
		if(!array_key_exists($name, $this->vars))
		{
			// It's not loaded, let's do it.
			$CONFIG = array();

			require_once $file;

			$this->vars[$name] = $CONFIG;
		}
		
		// Return the loaded file
		return $this->vars[$name];
	}

	private function emulate_PI($file)
	{
		// PATH_INFO Emulator
		
		// Parse our board url
		$url = parse_url($this->vars['root']['root_url']);

		// Do we have a path?
		if(!isset($url['path']))
		{
			// To Keep things uniform, we should have one..
			$url['path'] = '/';
		}

		// These should be removed from the URL
		$strip = $url['path'] . $file;
		$strip2 = $url['path'];
		$pathinfo = str_replace(array($strip, $strip2), '', $_SERVER['REQUEST_URI']);

		// If it's empty don't set PATH_INFO
		if($pathinfo != NULL)
		{
			if(SMIT_DEBUG) header('X-Emulated-PI: Yes');

			$this->debug->notice('PATH_INFO Unsupported, Using an emulated version of "PATH_INFO"');
			$_SERVER['PATH_INFO'] = $pathinfo;
		}
	}

	function routeRequests()
	{
		// act or segment 1 is supposed to be the application
		$app = preg_replace('/[^a-zA-Z0-9-_]/', '', $this->URI(1, 'app'));

		// Passing -1 will fetch an array of segments
		$uri = $this->URI(-1);

		// Allow a Magic 'debug' segment to trigger debugging
		// Only works if SMIT_DEBUG or Debugging mode is ON
		if(SMIT_DEBUG)
		{
			// It will try to load an application called debug..
			// But wait debugging is on, load the default app then...
			if($app == 'debug')
			{
				define('DEBUG_ON', 1);
				// Load the default application
				$app = 'app';
			}
			else
			{
				// if any segment equals 'debug', turn on debugging
				if(in_array('debug', $uri))
				{
					define('DEBUG_ON', 1);
					/**
					 * Some apps call flush inside their logic statements,
					 * this shutdown function will ensure debugging is always displayed
					 */
					register_shutdown_function(array(&$this->debug, 'call'));
				}
			}
		}

		// If no segment is set load the default application
		// Or if the segment is set load what's in the segment
		$this->AppEngine(($app == '' ? 'app' : $app));
	}

	function can_cache()
	{
		// if the dummy driver is loaded, we aren't really caching
		// This may be use to someone
		return ($this->cache->driver == '/dev/null/' ? FALSE : TRUE);
	}

	function can_seo()
	{
		// So we can change this easily in the future
		if(!defined('RUN_GENERIC'))
		{
			if($this->vars['root']['friendly_uri'] == 1)
			{
				return TRUE;
			}
		}
		return FALSE;
	}

	function AppEngine($app, $args = array())
	{
		// The Application initiation engine

		$apppath = SMIT_SYS_PATH . 'apps/' . $app . EXT;
		
		// // Does the segment map to a valid file??
		if(file_exists($apppath))
		{
			// It's a valid application

			// Load the Parent class
			require_once SMIT_SYS_PATH . 'core/SmitApp' . EXT;

			// Load the Application
			require_once $apppath;
			$class = ucfirst(strtolower($app));

			// Ok, the file may exist, but is the class valid?
			if(!class_exists($class))
			{
				// Let's help out
				if(SMIT_DEBUG)
					$this->error('The Application "' . $app . '" classname does not match the directory.', E_CLASS_MISMATCH, 'Application Location: ' . $app . '/' .basename($apppath) . '<br />Class should be: ' . $class);

					$this->AppEngine('app');
			}
			else
			{
				// TODO: Search for bugs in Reflection
				$this->app = new $class;
				if(method_exists($this->app, 'init'))
				{
					$this->app->init();
				}
				$reflector = new ReflectionClass($class);
				$function = preg_replace('/[^a-zA-Z0-9_]/', '', $this->URI(2, 'mode'));
				if($function != NULL && method_exists($this->app, $function) && substr($function, 0, 1) != '_' && $function != 'init')
				{
					$function = $function;
				}
				else
				{
					$function = 'main';
				}
				$method = $reflector->getMethod($function);
				//var_dump($method->getParameters());
				$method_parms = array();
				foreach($method->getParameters() as $parm)
				{
					$method_parms[] = array('name' => $parm->name, 'type' => ($parm->isOptional() ? $parm->getDefaultValue() : FALSE));
				}
				$args = $this->URI(-1);
				if($this->can_seo())
				{
					unset($args[1]);
					unset($args[2]);
					if($method->isPublic())
					{
						$method->invokeArgs($this->app, $args);
					}
					//$method->invokeArgs($this->app, $args);
				}
				else
				{
					unset($args['app']);
					unset($args['mode']);
					$fixed_parms = array();
					foreach($method_parms as $val)
					{
						if(isset($args[$val['name']]))
						{
							$fixed_parms[] = $args[$val['name']];
						}
						else
						{
							$fixed_parms[] = $val['type'];
						}
					}
					$method->invokeArgs($this->app, $fixed_parms);
				}
				//$method->invokeArgs($this->app, $args);
				// This will radically change
				//$this->app->main();
			}
		}
		else
		{
			// If app.php doesn't exist check if we have a route to follow.
			$route = SMIT_SYS_PATH . 'apps/' . ($app == 'app' ? '' : $app . '/') . 'default' . EXT;
			
			// We have no route...
			if(!file_exists($route))
			{
				if(SMIT_DEBUG)
					$this->error('The Application "' . $app . '" could not be routed successfully.', E_MISSING_FILE, 'Application Path: ' . $apppath . '<br />Tried Using Route: ' . $route);
				// Application not found...load default
				$this->AppEngine('app');
			}
			else
			{
				// Yeah! We got a route!
				require_once $route;
				
				// $ROUTE_APP should be set in the route file
				
				// Now repeat the samething all over...
				$this->AppEngine($ROUTE_APP);
			}
		}
	}

	function parseManifest($app)
	{
		$parsedManifest = array();
		$manifest = SMIT_SYS_PATH . 'apps/' . $app . '/manifest' . EXT;
		if(!file_exists($manifest))
		{
			$this->error('Manifest for "' . $app . '" could not be found.', E_MISSING_FILE, 'Manifest Path: ' . $manifest);
		}
		$contents = explode(chr(10), $this->lib->strtounix(file_get_contents($manifest)));
		
		unset($contents[0]);
		
		foreach($contents as $line)
		{
			$colon = strpos($line, ':');
			$key = trim(substr($line, 0, $colon));
			$value = trim(substr($line, $colon + 1));

			$parsedManifest[$key] = $value;
		}
		return $parsedManifest;
	}

	function URI($segment, $key = NULL)
	{
		// If the segment is -1, then an array should be returned!
		if($segment !== -1)
		{
			if($this->can_seo())
			{
				if(!isset($_SERVER['PATH_INFO']))
				{
					return NULL;
				}
				$segments = explode('/', $_SERVER['PATH_INFO']);
				unset($segments[0]);
				if(substr(strrchr($_SERVER['PATH_INFO'], '/'), 1) == '')
				{
					unset($segments[count($segments)]);
				}
				if(!array_key_exists($segment, $segments))
				{
					return NULL;
				}
				return $segments[$segment];
			}
			else
			{
				if(array_key_exists($key, $_GET))
				{
					return $_GET[$key];
				}
				return NULL;
			}
		}
		else
		{
			if($this->can_seo())
			{
				if(!isset($_SERVER['PATH_INFO']))
				{
					return array();
				}
				$segments = explode('/', $_SERVER['PATH_INFO']);
				unset($segments[0]);
				if(substr(strrchr($_SERVER['PATH_INFO'], '/'), 1) == '')
				{
					unset($segments[count($segments)]);
				}
				return $segments;
			}
			else
			{
				return $_GET;
			}
		}
	}
	
	function load($class)
	{
		switch($class)
		{
			default:
				// this loads handlers from the running applications directory
				$classvar = strtolower($class);
				if(isset($this->$classvar))
				{
					return $this->$classvar;
				}

				$class_path = SMIT_SYS_PATH . 'apps/' . $this->appname . '/' . $class . EXT;

				if(!file_exists($class_path))
				{
					$this->error('Class: "' . basename($class_path, EXT) . '" has failed to load', E_MISSING_FILE, 'Full Path: ' . $class_path);
				}
				
				require_once $class_path;

				$class = ucfirst($classvar);

				if(!class_exists($class))
				{
					$this->error('Unable to create instance of "' . $class . '".', E_CLASS_MISMATCH, 'Class Location: ' . $class_path . '<br />Class should be: ' . $class);
				}

				$this->app->$classvar = new $class;
			break;
			case 'database':
				// Load the DB Layer
				if(is_object($this->db))
				{
					// One instance only!!
					return $this->db;
				}
				// Main DB Class
				require_once SMIT_SYS_PATH . 'database/dbMaster' . EXT;
				
				$name = (defined('RUN_GENERIC') ? $this->db_vars['db_driver'] : $this->vars['root']['db_driver']);
				
				// Load the Driver it Should extend the Master Class
				$driver = SMIT_SYS_PATH . 'database/drivers/' . $name . EXT;
				if(!file_exists($driver))
				{
					$this->error('Unknown Database Driver "' . $name . '", Please Check Permissions and File Location.', E_MISSING_FILE, 'Full Path: ' . $driver);
				}
				require_once SMIT_SYS_PATH . 'database/drivers/' . $name . EXT;
				
				$slavedb = 'driver' . ucfirst(strtolower($name));
				$this->db = new $slavedb;
				return $this->db->connect($this->db_vars);
			break;
			case 'corelib':
				if(is_object($this->lib))
				{
					return $this->lib;
				}
				// SmitLib class
				require_once SMIT_SYS_PATH . 'core/SmitLib' . EXT;

				$this->lib = SmitLib::getInstance();
			break;
			case 'session':
				if(is_object($this->session))
				{
					return $this->session;
				}
				// SmitSession class
				require_once SMIT_SYS_PATH . 'core/SmitSession' . EXT;

				$this->session = SmitSession::getInstance();
			break;
			case 'output':
				if(is_object($this->output))
				{
					// One instance only!!
					return $this->output;
				}
				// SmitOutput class
				require_once SMIT_SYS_PATH . 'core/SmitOutput' . EXT;

				$this->output = SmitOutput::getInstance();
			break;
			case 'debug':
				if(is_object($this->debug))
				{
					// One instance only!!
					return $this->debug;
				}
				// SmitDebug class
				require_once SMIT_SYS_PATH . 'core/SmitDebug' . EXT;

				$this->debug = SmitDebug::getInstance();
			break;
			case 'cache':
				if(is_object($this->cache))
				{
					// One instance only!!
					return $this->cache;
				}
				// Main Cache Class
				require_once SMIT_SYS_PATH . 'caching/cacheMaster' . EXT;
				
				// Load the Driver it Should extend the Master Class
				$driver = SMIT_SYS_PATH . 'caching/drivers/' . $this->vars['root']['cache_driver'] . EXT;
				if(!file_exists($driver))
				{
					$this->error('Unknown Cache Driver "' . $this->vars['root']['cache_driver'] . '", Please Check Permissions and File Location.', E_MISSING_FILE, 'Full Path: ' . $driver);
				}
				require_once SMIT_SYS_PATH . 'caching/drivers/' . $this->vars['root']['cache_driver'] . EXT;
				
				$this->cache = new cacheSlave;
			break;
			case 'bbcode':
				if(is_object($this->bbcode))
				{
					// One instance only!!
					return $this->bbcode;
				}
				// New BBCode Parser class
				require_once SMIT_SYS_PATH . 'core/NBBC' . EXT;

				$this->bbcode = new BBCode;
				$this->bbcode->SetWikiURL('http://en.wikipedia.org/wiki/');
				$this->bbcode->SetDetectURLs(TRUE);
			break;
			case 'sha2':
				if(is_object($this->sha2))
				{
					// One instance only!!
					return $this->sha2;
				}
				// Nanolink Solutions SHA256 Implentation
				require_once SMIT_SYS_PATH . 'core/SHA256' . EXT;

				$this->sha2 = new nanoSha2();
			break;
		}
		
	}

	function loadHandler($handler, $classname = NULL)
	{	
		// This loads handlers from the /sources/handlers/ directory
		// Useful for global handlers like the session, user, ssi etc.. handler
		$handler_path = SMIT_SYS_PATH . 'handlers/' . $handler . EXT;

		if(!file_exists($handler_path))
		{
			$this->error('Handler: "' . basename($handler_path, EXT) . '" has failed to load', E_MISSING_FILE, 'Full Path: ' . $handler_path);
		}
		
		require_once $handler_path;
		
		$class = ($classname == NULL) ? $handler . 'Handler' : $classname;
		$instance = strtolower($handler);

		$this->app->$instance = new $class;
		
	}

	function langSet($lang)
	{
		// Sets the language directory
		$this->lang_dir = $lang;
	}

	function lang($key)
	{
		/**
		 * This function accepts unlimited arguments
		 * The first argument is the language key to load
		 * The Second argument and so forth are the replacments for the string placeholders
		 */

		// get the parts of the string
		$parms = explode('.', $key);
		$parmsc = count($parms);
		$variable = $parms;
		unset($variable[0]);
		$variable = implode('.', $variable);

		// Allows us to use replacments without using an array to supply values :)
		$replacements = func_get_args();

		// The first argument would be the file, we don't need it
		unset($replacements[0]);

		$file = ($parmsc == 0 ? $key : $parms[0]);

		if(!isset($this->lang[$file]))
		{
			$lang_path = SMIT_SYS_PATH . 'lang/' . $this->lang_dir . '/' . $file . EXT;

			if(!file_exists($lang_path))
			{
				$this->error('Language File: "' . $file . '" could not be loaded.', E_MISSING_FILE, 'Full Path: ' . $lang_path);
			}
			
			require_once $lang_path;
			$this->lang[$file] = $LANG;
		}

		if($parmsc > 1)
		{
			if(isset($this->lang[$file][$variable]))
			{
				$parsed_str = vsprintf($this->lang[$file][$variable], $replacements);
				return $parsed_str;
			}
			else
			{
				return $variable . ' - Undef';
			}
		}
		return $this->lang[$file];
		
	}
}

?>